#include "XDemux.h"
#include <iostream>
#include "htime.h"
using namespace std;

extern "C" {
	#include "libavformat/avformat.h"
	#include "libavdevice/avdevice.h"
}

static double r2d(AVRational r)
{
	return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}

bool XDemux::Open(const char *url)
{
	Close();
  _url = url;
	//参数设置
	AVDictionary *opts = NULL;
	//设置rtsp流以tcp协议打开
	av_dict_set(&opts, "rtsp_transport", "tcp", 0);

	// 输入流的最大解码延迟（最大缓存时长）
	av_dict_set(&opts, "max_delay", "50", 0);

  // 自动重连
  av_dict_set(&opts, "reconnect", "1", 0);  // 启动重连机制
  av_dict_set(&opts, "reconnect_streamed", "1", 0); // 允许对流式媒体进行重连
  av_dict_set(&opts, "reconnect_delay_max", "5", 0); // 设置最大重连延迟时间/秒

	mux.lock();
	int re = avformat_open_input(
		&ic,
		url,
		0,  // 0表示自动选择解封器
		&opts //参数设置，比如rtsp的延时时间
	);
	if (re != 0)
	{
		mux.unlock();
		char buf[1024] = { 0 };
		av_strerror(re, buf, sizeof(buf) - 1);
		cout << "open " << url << " failed! :" << buf << endl;
		return false;
	}
	cout << "open " << url << " success! " << endl;

	//获取流信息 
	re = avformat_find_stream_info(ic, 0);

	//总时长 毫秒
	this->totalMs = ic->duration / (AV_TIME_BASE / 1000);
	cout << "totalMs = " << totalMs << endl;

	//打印视频流详细信息
	av_dump_format(ic, 0, url, 0);

	//获取视频流
	videoStream = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	if(videoStream >= 0)
	{
		printf("file:%s function:%s line:%d videoStream = %d\n", __FILE__, __FUNCTION__, __LINE__, videoStream);
		AVStream *as = ic->streams[videoStream];

		if(as == NULL)
		{
			printf("file:%s function:%s line:%d  as == NULL \n", __FILE__, __FUNCTION__, __LINE__);

		}

		if(as->codecpar == NULL)
		{
			printf("file:%s function:%s line:%d  as->codecpar == NULL\n", __FILE__, __FUNCTION__, __LINE__);
		}

		//printf("file:%s function:%s line:%d %d\n", __FILE__, __FUNCTION__, __LINE__, ic->streams[videoStream]->codecpar);
		width = as->codecpar->width;
		height = as->codecpar->height;

		cout << "=======================================================" << endl;
		cout << videoStream << ": video info" << endl;
		cout << "codec_id = " << as->codecpar->codec_id << endl;
		cout << "format = " << as->codecpar->format << endl;
		cout << "width=" << as->codecpar->width << endl;
		cout << "height=" << as->codecpar->height << endl;
		//帧率 fps 分数转换
		cout << "video fps = " << r2d(as->avg_frame_rate) << endl;
	}
	
	//获取音频流
	audioStream = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
	if(audioStream >= 0)
	{
		cout << "=======================================================" << endl;
		cout << audioStream << ": audio info" << endl;
		AVStream* as = ic->streams[audioStream];
		sampleRate = as->codecpar->sample_rate;
		channels = as->codecpar->channels;

		cout << "codec_id = " << as->codecpar->codec_id << endl;
		cout << "format = " << as->codecpar->format << endl;
		cout << "sample_rate = " << as->codecpar->sample_rate << endl;
		cout << "channels = " << as->codecpar->channels << endl;
		//一帧数据？？ 单通道样本数 
		cout << "frame_size = " << as->codecpar->frame_size << endl;
	}
	
	mux.unlock();

	return true;
}
//清空读取缓存
void XDemux::Clear()
{
	mux.lock();
	if (!ic)
	{
		mux.unlock();
		return ;
	}
	//清理读取缓冲
	avformat_flush(ic);
	mux.unlock();
}

void XDemux::Close()
{
	mux.lock();
	if (!ic)
	{
		mux.unlock();
		return;
	}
	avformat_close_input(&ic);
	//媒体总时长（毫秒）
	totalMs = 0;
	mux.unlock();
}

//seek 位置 pos 0.0 ~1.0
bool XDemux::Seek(double pos)
{
	mux.lock();
	if (!ic)
	{
		mux.unlock();
		return false;
	}
	//清理读取缓冲
	avformat_flush(ic);

	long long seekPos = 0;
	seekPos = ic->streams[videoStream]->duration * pos;
	int re = av_seek_frame(ic, videoStream, seekPos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
	mux.unlock();
	if (re < 0) return false;
	return true;
}
//获取视频参数  返回的空间需要清理  avcodec_parameters_free
AVCodecParameters *XDemux::CopyVPara()
{
	mux.lock();
	if (!ic)
	{
		mux.unlock();
		return NULL;
	}
	AVCodecParameters *pa = avcodec_parameters_alloc();
	avcodec_parameters_copy(pa, ic->streams[videoStream]->codecpar);
	mux.unlock();
	return pa;
}

//获取音频参数  返回的空间需要清理 avcodec_parameters_free
AVCodecParameters *XDemux::CopyAPara()
{
	mux.lock();
	if (!ic || audioStream < 0)
	{
		mux.unlock();
		return NULL;
	}
	AVCodecParameters *pa = avcodec_parameters_alloc();
	avcodec_parameters_copy(pa, ic->streams[audioStream]->codecpar);
	mux.unlock();
	return pa;
}
bool XDemux::IsAudio(AVPacket *pkt)
{
	if (!pkt) return false;
	if (pkt->stream_index == videoStream)
		return false;
	return true;

}
AVPacket *XDemux::ReadVideo()
{
	mux.lock();
	if (!ic) //容错
	{
		mux.unlock();
		return 0;
	}
	mux.unlock();

	AVPacket *pkt = NULL;
	//防止阻塞
	for (int i = 0; i < 20; i++)
	{
		pkt = Read();
		if (!pkt)break;
		if (pkt->stream_index == videoStream)
		{
			break;
		}
		av_packet_free(&pkt);
	}
	return pkt;
}
//空间需要调用者释放 ，释放AVPacket对象空间，和数据空间 av_packet_free
AVPacket *XDemux::Read()
{
	mux.lock();
	if (!ic) //容错
	{
		mux.unlock();
		return 0;
	}
	AVPacket *pkt = av_packet_alloc();
	//读取一帧，并分配空间
  printf("file:%s function:%s line:%d\n", __FILE__, __FUNCTION__, __LINE__);
	int re = av_read_frame(ic, pkt);
  printf("file:%s function:%s line:%d\n", __FILE__, __FUNCTION__, __LINE__);
	if (re != 0)
	{
		mux.unlock();
		av_packet_free(&pkt);
    char err[1024];
    av_make_error_string(err, sizeof(err), re);
    printf("ERROR:ret = %d, %s\n", re,err);

    if (re == AVERROR_EOF) {
      printf("***************************************Warning: try to reconnect!");
      Close();
      Open(_url.c_str());
    }
		return 0;
	}
	//pts转换为毫秒
	//cout << "pkt->pts1:" << pkt->dts << endl;
	//cout << "ic->streams[pkt->stream_index]->time_base:" << r2d(ic->streams[pkt->stream_index]->time_base) << endl;;
	pkt->pts = pkt->pts*(1000 * (r2d(ic->streams[pkt->stream_index]->time_base)));
	pkt->dts = pkt->dts*(1000 * (r2d(ic->streams[pkt->stream_index]->time_base)));
	mux.unlock();
	cout << "pkt->pts2:" << pkt->dts << endl;
  printf("file:%s function:%s line:%d time1 = %lld\n", __FILE__, __FUNCTION__, __LINE__, gethrtime_us() / 1000);

	return pkt;

}

XDemux::XDemux()
{
	static bool isFirst = true;
	static std::mutex dmux;
	dmux.lock();
	if (isFirst)
	{
		//初始化封装库
		//av_register_all();

		//初始化网络库 （可以打开rtsp rtmp http 协议的流媒体视频）
		avformat_network_init();
		//avdevice_register_all();
		isFirst = false;
	}
	dmux.unlock();
}


XDemux::~XDemux()
{
}
